<?php
/**
 * 数据包装类：为了方便的使用Zend_Db_Adapter或者Zend_Db_Table，
 *             使用该数据包装类，可以对_REQUEST数据进行检测，找到和数据库
 *             里对应的字段，对该输入字段进行验证，然后再交给数据库对象进
 *             行进一步处理。
 *
 * @author  wuxi dot cn at gmail dot com
 * @version 0.24 at 2008.3.24
 */
class Wrapper 
{
	/* 表名 */
	protected $table;
	/* 主键 */
	protected $pk;
	/* 表的所有字段名，用|分隔，string */
	protected $field; // database fields
	/* 表中各字段对应的描述名字，例如字段"Tel"对应描述名为："电话" */
	protected $name;
	/* 表的字段约束，正则表达式表示，array */
	protected $constraint;

	/// 上次出错字段。string
	public $errField;
	/// 出错提示。string
	public $error;


	/**
	 * 构造函数。
	 *
	 * @param na string, 表名。
	 * @param pk string, 主键名。
	 * @param fd string, 所有字段描述，用'|'分隔各字段。fd格式如下：
	 *           'FD|FD|...'，FD格式：'Field:Name'，:Name可省。
	 *           例如：'Detail:详细信息|Tel|Sex:性别'。
	 * @param cs array, (constraint)数据库表各个字段的约束。
	 *           数组为字段名和perl兼容的正则式对("field_name"=>"regx")格
	 *           式，例如：'/(\w{6,})/' 表示6个字符以上。
	 */
	public function __construct($na, $pk, $fd, $cs=array())
	{
		$this->table = $na;
		$this->pk = $pk;
		$this->constraint = $cs;
		$arr = explode('|', $fd);
		foreach ($arr as $fds) {
			$tmp = explode(':', $fds);
			$this->field .= $tmp[0] . '|';
			if (array_key_exists(1, $tmp)) {
				$k = $tmp[0];
				$this->name[$k] = $tmp[1];
			}
		}
	}

	/**
	 * 从数组中过滤出表中存在字段。
	 *
	 * @param data  array, 待处理的数据。
	 * @param limit bool, 筛选结果数目限制，=0为不限制。
	 *
	 * @return array, 过滤后只包含表里出现的字段的数组。
	 */
	public function Filter($data, $limit=0)
	{
		$result=array();
		$n=0;
		$limit = $limit==0?9999:$limit;
		foreach ($data as $k => $v) {
			if (preg_match("/\b$k\b/", $this->field))
				$result[$k]=$v;
			if (++$n>=$limit) break;
		}
		return $result;
	}

	/**
	 * 过滤并检测字段是否符合规范。
	 *
	 * @param data   array, 待检测的数组。
	 * 
	 * @return array|int, 成功返回array，失败返回错误码：
	 *         -1: 字段不符合规范。出错字段、出错信息同上。
	 */
	public function Check($data, $ignore=false)
	{
		$data = self::Filter($data, 0);

		foreach ($this->constraint as $k => $v) {

			if (!array_key_exists($k, $data))
				continue;
			
			if (!preg_match($v, $data[$k])) {
				if (array_key_exists($k, $this->name))
					$tmp = $this->name[$k];
				else 
					$tmp = $k;
				$this->fieldError = $k;
				$this->error = "'$tmp'输入不正确。";
				return -1;
			}
			
		} // end of foreach

		return $data;
	}

	/**
	 * 插入一行数据。(自动过滤data中多余的项)
	 *
	 * @param db    Zend_Db_Adapter_Abstract。
	 * @param data  array, 待插入的数组。
	 *
	 * @return int, 成功则返回所插入那行的id；失败返回-1。
	 */
	public function Insert(Zend_Db_Adapter_Abstract $db, array $data)
	{
		$d = self::Filter($data);
		/*
		$field = '';
		$value = '';
		$sep = '';
		foreach ($d as $k => $v) {
			$field .= "$sep`$k` ";
			$value .= "$sep:$k ";
			$sep = ',';
		}
		$result = $db->query(
		*/
		$result = $db->insert($this->table, $d);
		if ($result==0) return -1;
		return $db->lastInsertId();
	}
	/**
	 * 根据id，更新一行数据。
	 *
	 * @param db    Zend_Db_Adapter_Abstract。
	 * @param data  array, 待更新的数据。
	 * @param id    int, Id.
	 *
	 * @return int 成功返回1，失败返回0。
	 */
	public function Update(Zend_Db_Adapter_Abstract $db, $id, array $data)
	{
		$d = self::Filter($data, 0);
		$id = intval($id);
		$result = $db->update($this->table, $d, "{$this->pk}={$id}");
		return $result;
	}

	/**
	 * 删除。
	 *
	 * @param db    Zend_Db_Adapter_Abstract。
	 * @param id    int, Id.
	 *
	 * @return int 成功返回1，失败返回0。
	 */
	public function Delete(Zend_Db_Adapter_Abstract $db, $id)
	{
		$id = intval($id);
		$result = $db->delete($this-table, "{$this->pk}={$id}");
		return $result;
	}

	// 用于escape和unescape函数。
	static protected $trans=array('<'=>'&lt;', '>'=>'&gt;',
			'\''=>'&#39;', '"'=>'&quot;', '&'=>'&amp;', ' '=>'&nbsp;',
			'/'=>'&#47;', "\n"=>'<br />', "\r"=>'',
			"\t"=>'&nbsp;&nbsp;&nbsp;&nbsp;'
			);

	/**
	 * 特殊符号=>HTML记号转换，类似php中的htmlentities函数。
	 *
	 * @param data string, 待转换的字符串。
	 *
	 * @return string, 转换后的结果。
	 */
	static function Escape($data)
	{
		return strtr($data, self::$trans);
	}

	/**
	 * html记号=>特殊符号转换。self::Escape()函数的逆函数。
	 *
	 * @param data string, 待转换的字符串。
	 *
	 * @return string, 转换后的结果。
	 */
	static function Unescape($data)
	{
		$trans=array_flip(self::$trans);
		return strtr($data, $trans);
	}

	/**
	 * 识别出链接。
	 */
	static function Link($data)
	{
		return preg_replace('/((?:https?|mailto).*?)(&nbsp;|<br|\'|\"|$)/',
				'<a href="\1" target="_blank">\1</a>\2', 
				$data);
	}

	/**
	 * 去除<script>标签。
	 */
	static function Unscript($data)
	{
		return preg_replace('/(<\/?)script([^>]*)>)', 
				'&lt;script\2&gt;', $data);
	}

	/**
	 * 去除<iframe>标签。
	 */
	static function Uniframe($data)
	{
		return preg_replace('/(<\/?)iframe([^>]*)>)', 
				'&lt;iframe\2&gt;', $data);
	}

};
?>
